using System;
using System.Collections.Generic;
using System.Text;

namespace Weazel.Microblaze.Elf
{
  public class ElfSectionHeader : ElfFormatEntity
  {
    #region Enumerations
    public enum Sh_Type
    {
      SHT_NULL = 0,
      SHT_PROGBITS = 1,
      SHT_SYMTAB   = 2,
      SHT_STRTAB   = 3,
      SHT_RELA     = 4,
      SHT_HASH     = 5,
      SHT_DYNAMIC  = 6,
      SHT_NOTE     = 7,
      SHT_NOBITS   = 8,
      SHT_REL      = 9,
      SHT_SHLIB    = 10,
      SHT_DYNSYM   = 11,
      SHT_LOPROC   = 0x70000000,
      SHT_HIGPROC  = 0x7fffffff,
      SHT_LOUSER   = int.MinValue, // 0x80000000
      SHT_HIUSER   = -1            // 0xffffffff
    }
    #endregion

    private uint sh_name;
    private Sh_Type sh_type;
    private uint sh_flags;    
    private uint sh_addr;
    private uint sh_offset;
    private uint sh_size;
    private uint sh_link;    
    private uint sh_info;
    private uint sh_addralign;    
    private uint sh_entsize;

    private ElfFormatEntity associatedEntity;

    private byte[] content;

    #region Properties
    public uint Sh_name
    {
      get { return sh_name; }
    }

    public Sh_Type Sh_type
    {
      get { return sh_type; }
    }

    public uint Sh_flags
    {
      get { return sh_flags; }
    }

    public uint Sh_addr
    {
      get { return sh_addr; }    
    }

    public uint Sh_offset
    {
      get { return sh_offset; }
    }

    public uint Sh_size
    {
      get { return sh_size; }
    }

    public uint Sh_link
    {
      get { return sh_link; }
    }

    public uint Sh_info
    {
      get { return sh_info; }
    }

    public uint Sh_addralign
    {
      get { return sh_addralign; }
    }

    public uint Sh_entsize
    {
      get { return sh_entsize; }
    }

    public string Name
    {
      get { return ElfFile.GetNameFromMainStringTable(this.sh_name); }
    }

    public ElfFormatEntity AssociatedEntity
    {
      get { return associatedEntity; }
      set { associatedEntity = value; }
    }

    public int Index
    {
      get { return ElfFile.SectionHeaders.IndexOf(this); }
    }

    public byte[] Content
    {
      get
      {
        return content;
      }
    }

    #endregion

    public ElfSectionHeader(ElfFile elfFile)
      : base(elfFile)
    {
    }

    public void Read()
    {
      this.sh_name = ReadElf32Word(ElfFile.Reader);
      this.sh_type = (Sh_Type) ReadElf32Word(ElfFile.Reader);
      this.sh_flags = ReadElf32Word(ElfFile.Reader);
      this.sh_addr = ReadElf32Word(ElfFile.Reader);
      this.sh_offset = ReadElf32Word(ElfFile.Reader);
      this.sh_size = ReadElf32Word(ElfFile.Reader);
      this.sh_link = ReadElf32Word(ElfFile.Reader);
      this.sh_info = ReadElf32Word(ElfFile.Reader);
      this.sh_addralign = ReadElf32Word(ElfFile.Reader);
      this.sh_entsize = ReadElf32Word(ElfFile.Reader);

      // save current position
      long position = ElfFile.Reader.BaseStream.Position;

      // set to position of section content
      ElfFile.Reader.BaseStream.Seek(sh_offset, 0);

      // read the content
      content = ElfFile.Reader.ReadBytes((int)sh_size);

      // restore the stream position
      ElfFile.Reader.BaseStream.Seek(position, 0);
    }

    public override string ToString()
    {
      StringBuilder str = new StringBuilder();
      str.AppendFormat("sh_name      : {0} \n", Name);

      str.AppendFormat("sh_type      : {0} \n", this.sh_type);
      str.AppendFormat("sh_flags     : {0} \n", this.sh_flags);
      str.AppendFormat("sh_addr      : 0x{0:X} \n", this.sh_addr);
      str.AppendFormat("sh_offset    : 0x{0:X} \n", this.sh_offset);
      str.AppendFormat("sh_size      : {0} \n", this.sh_size);
      str.AppendFormat("sh_link      : {0} \n", this.sh_link);
      str.AppendFormat("sh_info      : {0} \n", this.sh_info);
      str.AppendFormat("sh_addralign : {0} \n", this.sh_addralign);
      str.AppendFormat("sh_entsize   : {0} \n", this.sh_entsize);

      return str.ToString();
    }
  }
}
